\documentclass[a4paper]{article}
\usepackage{fontspec,xunicode,xltxtra} % 加载 xetex 的宏包 
\usepackage{graphicx,amsmath}
\usepackage{makeidx}
\usepackage{array}
\usepackage{listings,xcolor}
\setsansfont[Mapping=tex-text]{Optima LT} % 使用 XeTeX 的 text-mapping 方案，正确显示LaTeX 样式的双引号（`` ''）
%\setmonofont{Consolas}

\setlength{\voffset}{-1cm}
\setlength{\hoffset}{-1cm}
\addtolength{\textwidth}{2cm}
\addtolength{\textheight}{2cm}
\linespread{1.3}

\lstset{language=VHDL, numbers=left, numberstyle=\tiny, breaklines, basicstyle=\ttfamily}

% 以下是中文字体的设置
\usepackage[slantfont，boldfont，xCJKnumber，xCJKtextspaces]{xeCJK} % 加载 xeCJK，允许斜体、粗体和 CJK 数字以及 CJK 对空格的设置
\setCJKmainfont[BoldFont={Adobe Heiti Std}, ItalicFont={Adobe Kaiti Std}]{Adobe Song Std}
\setCJKsansfont{Adobe Fangsong Std} 
\setCJKmonofont{Adobe Song Std}
\author{李骥扬\\2009011329 \and 蓝昶\\2009011352 \and 余舟迅\\2009011343}
\title{\textbf{计算机组成原理实验：存储器I/O}}
\begin{document}
\maketitle

\section{实验目的}
\begin{enumerate}
\item 熟悉实验机外围IO
\item 掌握RAM、ROM访问时序
\item 掌握8251工作原理，实现串口通讯
\end{enumerate}

\section{实验原理}
本实验是用TEC2008提供的内存和8251访问控制电路，只需要控制MIO、REQ、WE三个信号、基本地址线的值、基本数据线的值、扩展地址线的值、扩展数据线的值、扩展RAM的写信号、扩展RAM的输出使能、扩展RAM的片选信号就可以了访问这些内存和IO了。具体来说就是：

\begin{enumerate}
\item 读基本ROM

MIO=0 REQ=0 WE=1，地址送基本地址线，基本数据线给高阻态信号，扩展RAM写信号给1，扩展RAM输出使能给1，扩展RAM片选信号给0，其他信号任意；
\item 读基本RAM

MIO=0 REQ=0 WE=1，地址送基本地址线，基本数据线给高阻态信号，扩展RAM写信号给1，扩展RAM输出使能给1，扩展RAM片选信号给0，其他信号任意；
\item 写基本RAM 

MIO=0 REQ=0 WE=0，地址送基本地址线，基本数据线给要写入的数据，扩展RAM写信号给1，扩展RAM输出使能给1，扩展RAM片选信号给0，其他信号任意；
\item 读扩展RAM

MIO=1，地址送扩展地址线，扩展数据线给高阻态信号，扩展RAM写信号给1，扩展RAM输出使能给0，扩展RAM片选信号给0，其他信号任意；
\item 写扩展RAM 

MIO=1，地址送扩展地址线，扩展数据线给要写入的值，扩展RAM写信号给0，扩展RAM输出使能给0，扩展RAM片选信号给0，其他信号任意；
\item 读串口1控制寄存器

MIO=0 REQ=1 WE=1，基本地址线送0081，基本数据线给高阻态信号，扩展RAM写信号给1，扩展RAM输出使能给1，扩展RAM片选信号给0，其他信号任意；
\item 写串口1控制寄存器

MIO=0 REQ=1 WE=0，基本地址线送0081，基本数据线给要写入的数据，扩展RAM写信号给1，扩展RAM输出使能给1，扩展RAM片选信号给0，其他信号任意；
\item 读串口1状态寄存器 

MIO=0 REQ=1 WE=1，基本地址线送0080，基本数据线给高阻态信号，扩展RAM写信号给1，扩展RAM输出使能给1，扩展RAM片选信号给0，其他信号任意；
\item 写串口1状态寄存器 

MIO=0 REQ=1 WE=0，基本地址线送0080，基本数据线给要写入的数据，扩展RAM写信号给1，扩展RAM输出使能给1，扩展RAM片选信号给0，其他信号任意；
\item 读串口2控制寄存器 

MIO=0 REQ=1 WE=1，基本地址线送0091，基本数据线给高阻态信号，扩展RAM写信号给1，扩展RAM输出使能给1，扩展RAM片选信号给0，其他信号任意；
\item 写串口2控制寄存器 

MIO=0 REQ=1 WE=0，基本地址线送0091，基本数据线给要写入的数据，扩展RAM写信号给1，扩展RAM输出使能给1，扩展RAM片选信号给0，其他信号任意；
\item 读串口2状态寄存器 

MIO=0 REQ=1 WE=1，基本地址线送0090，基本数据线给高阻态信号，扩展RAM写信号给1，扩展RAM输出使能给1，扩展RAM片选信号给0，其他信号任意；
\item 写串口2状态寄存器 

MIO=0 REQ=1 WE=0，基本地址线送0090，基本数据线给要写入的数据，扩展RAM写信号给1，扩展RAM输出使能给1，扩展RAM片选信号给0，其他信号任意。
\end{enumerate}

\section{实现原理}
\subsection{状态}
\begin{lstlisting}
	type enum_state is (
		reset_state, 
		read_rom, buf_read_rom, write_ram, eof_write_ram, 
		
		start_serial_loop, serial_loop,
		push_method_word, push_command_word,
		
		read_ram, buf_read_ram, write_serial_low, write_serial_high,
		eof_write_serial, final
	);
\end{lstlisting}
\subsection{数据缓冲区}
如果\texttt{Data}或者\texttt{Ex\_Data}的值发生改变，那么把新的值读入信号量\texttt{data\_buf}或者\texttt{data\_buf\_ex}中。
\begin{lstlisting}
	process(data)
	begin
		data_buf <= data;
	end process;
	
	process(Ex_Data)
	begin
		data_buf_ex <= Ex_Data;
	end process;
\end{lstlisting}
\subsection{操作标志位}
为了及时改变操作标志位，执行对应的功能，我们使用一个信号\texttt{func}记录要执行的操作。如果信号发生变化，则改变相应的标志位。
\begin{lstlisting}
	process(func)
	begin
		if (func=read_ex_ram) then
			RAM_WE <= '1';
			RAM_OE <= '0';
			Ex_Addr <= addr_buf(12 downto 0);
			Ex_Data <= (others => 'Z');
		end if;
		if (func=write_ex_ram) then
			RAM_WE <= '0';
			RAM_OE <= '0';
			Ex_Addr <= addr_buf(12 downto 0);
			Ex_Data <= data_reg;
		end if;
		if (func=read_rom_ram) then
			MIO <= '0';
			REQ <= '0';
			WE  <= '1';
			RAM_WE <= '1';
			RAM_OE <= '1';
			Addr <= addr_buf;
			Data <= (others => 'Z');
		end if;
		if (func=write_ram) then
			MIO <= '0';
			REQ <= '0';
			WE  <= '0';
			RAM_WE <= '1';
			RAM_OE <= '1';
			Addr <= addr_buf;
			Data <= data_reg;
		end if;
		if (func=read_serial) then
			MIO <= '0';
			REQ <= '1';
			WE  <= '1';
			RAM_WE <= '1';
			RAM_OE <= '1';
			Addr <= addr_buf;
			Data <= (others => 'Z');
		end if;
		if (func=write_serial) then
			MIO <= '0';
			REQ <= '1';
			WE  <= '0';
			RAM_WE <= '1';
			RAM_OE <= '1';
			Addr <= addr_buf;
			Data <= data_reg;
		end if;
	end process;
\end{lstlisting}
\subsection{状态机设计}
在每个时钟上升沿，程序判断当前状态机的状态值。由于代码较长，本实验报告给出具体流程和描述，详细细节请参阅附带的VHDL代码源文件。
\subsubsection{读取ROM}
设置地址。通过将\texttt{func}置为\texttt{read\_rom\_ram}，改变各个标志位为读取基本ROM时的情况，再将状态\texttt{state}设置为\texttt{buf\_read\_rom}。这样在下一个时钟上升沿，寄存器\texttt{data\_reg}将读到ROM的值。最后将状态\texttt{state}设置为\texttt{write\_ram}
\subsubsection{写RAM}
根据输出串口的不同，将数据寄存器的值加1或者加2。设置RAM的地址。通过将\texttt{func}置为\texttt{write\_ram}/\texttt{write\_ex\_ram}，改变各个标志位为读取基本/扩展RAM时的情况，写入RAM之中。如果计数器\texttt{count}的值没有到达指定的读取长度，那么回到读取ROM的状态继续读写。否则将计数器置零，进入写串口状态。
\subsubsection{写串口}
由于写串口之前需要判断当前串口是否可写，因此我们设置了两个状态\texttt{start\_serial\_loop}和\texttt{serial\_loop}，用于循环判断串口状态。判断之前，需要用一个信号\texttt{back\_state}记录当前状态，再进入\texttt{start\_serial\_loop}。如果串口可写，那么再回到\texttt{back\_state}记录的状态。

写串口之前需要先写入0x4E和0x37控制串口，注意每次写入之前都要用{start\_serial\_loop}判断串口是否可写。然后循环读取RAM（与读取ROM的方法类似，故不赘述），每读取一次（16位）都要分两次写入串口，每次都要判断串口是否可写。

\section{接口}
\begin{lstlisting}
entity memio is
    Port (    clk : in  STD_LOGIC;
              Reset : in  STD_LOGIC;
			  Addr : out STD_LOGIC_VECTOR(15 downto 0);
			  Data : inout STD_LOGIC_VECTOR(15 downto 0);
			  EX_Addr : out STD_LOGIC_VECTOR(12 downto 0);
			  EX_Data : inout STD_LOGIC_VECTOR(15 downto 0);
			  MIO : out STD_LOGIC;
			  REQ : out STD_LOGIC;
			  WE  : out STD_LOGIC;
			  RAM_OE : out STD_LOGIC;
			  RAM_WE : out STD_LOGIC
			);
end memio;
\end{lstlisting}
\section{调试与故障排除}
\begin{enumerate}
\item 使用串口调试工具，在计算机上查看串口输出
\item 需要注意实验箱上的RAM写保护跳线，否则无法写入RAM
\end{enumerate}

\section{实验收获}
\begin{enumerate}
\item 熟悉了RAM、ROM访问时序
\item 掌握了串口通讯
\end{enumerate}
\end{document}
